#include "Game.h"
#include "allegro5/allegro5.h"
#include "TitleGenerator.h"

Character* Game::hero = nullptr;
int Game::tps = 60;
int Game::ticks = 0;
int Game::fps = 0;
Phrase* Game::title = nullptr;

Game::Game(Renderer* aRenderer)
{
	timer = al_create_timer(1.0 / tps);
	al_start_timer(timer);

	seed = time(NULL);
	srand(seed);
	nextSeed = rand();
	inputLocked = false;
	isDynamic = true;

	exitFlag = false;

	renderer = aRenderer;
	worldMap = nullptr;
	currMap = nullptr;
	transition = nullptr;

	title = Phrase::CreatePhrase(TitleGenerator::GenerateTitle());
	title->AddOption("New game",nullptr);
	title->AddOption("Controls",nullptr);
	title->AddOption("About",nullptr);
	title->AddOption("Exit",nullptr);

	hero = new Character(JOB_UNEMPLOYED,rand()%BIOME_END,true);
	hero->mood = MOOD_NONE;
}

Game::~Game()
{
	if(worldMap) delete worldMap;
	if(transition != nullptr) delete transition;
	if(hero)
	{
		for(unsigned int i=0; i<hero->quests.size(); i++)
			delete hero->quests[i];
		delete hero;
	}
	al_destroy_timer(timer);
}

void Game::CreateGame()
{
	srand(nextSeed);
	nextSeed = rand();

	worldMap = new WorldMap(hero,rand());
	Node heroSpawn = worldMap->GetHeroSpawn();
	hero->SetPosition(heroSpawn.x,heroSpawn.y);

	currMap = worldMap;
	renderer->SetMap(currMap);

	transition = new Transition(&isDynamic);
	renderer->SetTransition(transition);
	transition->StartTransition(TRANSITION_FADE, false, 0.5f, true);

	menu = new Menu(hero);
	mainMenu = menu;
	renderer->SetMenu(&menu);
}

void Game::RegenerateWorld()
{
	delete title->nextPhrase;
	title->nextPhrase = nullptr;
	title->selected = 100;

	delete mainMenu;
	delete transition;
	delete worldMap;
}

struct QuickControl
{
	int direction;
	int modX;
	int modY;

	void operator()(int aDir, int aModX, int aModY)
	{
		direction = aDir;
		modX = aModX;
		modY = aModY;
	}
};

void Game::ProceedInput(Input* aInput)
{
	if(inputLocked) return;

	// Title and generation screen
	if(title->nextPhrase == nullptr)
	{
		if(aInput->KeyPressed(ALLEGRO_KEY_UP) || aInput->KeyPressed(ALLEGRO_KEY_W))
		{
			if(title->selected > 0 && title->selected < 99)
				title->selected--;
		}
		if(aInput->KeyPressed(ALLEGRO_KEY_DOWN) || aInput->KeyPressed(ALLEGRO_KEY_S))
		{
			if(title->selected < title->options.size()-1 && title->selected < 99)
				title->selected++;
		}
		if(aInput->KeyPressed(ALLEGRO_KEY_ENTER) || aInput->KeyPressed(ALLEGRO_KEY_SPACE))
		{
			if(title->selected == title->options.size()-1)
				exitFlag = true;
			if(title->selected < 99)
			{
				if(title->selected == 0)
					title->selected = 100;
				if(title->selected == 1)
					title->selected = 200;
				if(title->selected == 2)
					title->selected = 300;
			}
			else
				if(title->selected == 200 || title->selected == 300)
					title->selected = 0;
		}
		if(aInput->KeyPressed(ALLEGRO_KEY_ESCAPE))
		{
			exitFlag = true;
		}

		return;
	}

	if(menu->GetVisible())
	{
		menu->ProceedInput(aInput);
	}
	else
	{
		// Hero controls
		QuickControl control = {-1,0,0};
		if(aInput->KeyDown(ALLEGRO_KEY_W)) control(UP,0,-1);
		if(aInput->KeyDown(ALLEGRO_KEY_S)) control(DOWN,0,1);
		if(aInput->KeyDown(ALLEGRO_KEY_A)) control(LEFT,-1,0);
		if(aInput->KeyDown(ALLEGRO_KEY_D)) control(RIGHT,1,0);
		hero->isGhost = aInput->KeyDown(ALLEGRO_KEY_LCTRL);

		if(control.direction != -1)
		{
			renderer->GetMapDrawer()->SetHeroLock(true);

			int x = hero->GetPositionX()+control.modX;
			int y = hero->GetPositionY()+control.modY;
			bool pass = currMap->IsPassable(x,y,hero->isGhost);
			if(!currMap->IsValid(x,y)) pass = true;

			if(pass && !hero->IsMoving()) hero->Move(control.direction);
			if(!pass && !hero->IsMoving()) hero->SetFacing(control.direction);
		}

		if(aInput->KeyReleased(ALLEGRO_KEY_W) ||
				aInput->KeyReleased(ALLEGRO_KEY_A) || 
				aInput->KeyReleased(ALLEGRO_KEY_S) ||
				aInput->KeyReleased(ALLEGRO_KEY_D))
		{
			hero->Stop();
		}

		// Map controls
		if(aInput->KeyDown(ALLEGRO_KEY_UP)) renderer->GetMapDrawer()->pan(UP);
		if(aInput->KeyDown(ALLEGRO_KEY_DOWN)) renderer->GetMapDrawer()->pan(DOWN);
		if(aInput->KeyDown(ALLEGRO_KEY_RIGHT)) renderer->GetMapDrawer()->pan(RIGHT);
		if(aInput->KeyDown(ALLEGRO_KEY_LEFT)) renderer->GetMapDrawer()->pan(LEFT);
		if(aInput->KeyPressed(ALLEGRO_KEY_G)) renderer->GetMapDrawer()->toggleGrid();
		if(aInput->KeyPressed(ALLEGRO_KEY_M)) renderer->GetMapDrawer()->toggleMinimap();
		if(aInput->KeyPressed(ALLEGRO_KEY_F)) renderer->ToggleFullscreen();
		if(aInput->MouseReleased(0)) renderer->GetMapDrawer()->mouseMiniMapPan(aInput->GetMouseX(),aInput->GetMouseY());
		if(aInput->MouseScrolled() > 0) renderer->GetMapDrawer()->zoom(true);
		if(aInput->MouseScrolled() < 0) renderer->GetMapDrawer()->zoom(false);

		// Pan unlock
		if(aInput->KeyPressed(ALLEGRO_KEY_UP)
		|| aInput->KeyPressed(ALLEGRO_KEY_DOWN)
		|| aInput->KeyPressed(ALLEGRO_KEY_LEFT)
		|| aInput->KeyPressed(ALLEGRO_KEY_RIGHT))
		{
			renderer->GetMapDrawer()->SetHeroLock(false);
		}

		// Item menu
		if(aInput->KeyReleased(ALLEGRO_KEY_I))
		{
			if(menu != mainMenu)
			{
				delete menu;
				menu = mainMenu;
			}
			menu->SetVisible(true);
		}

		// Action
		if(aInput->KeyReleased(ALLEGRO_KEY_SPACE))
		{
			Character* interlocutor = currMap->GetHeroInterlocutor();
			if(interlocutor != nullptr)
			{
				menu = new Menu(hero, interlocutor);
				menu->SetVisible(true);
			}

			TileAction* tileAction = currMap->GetHeroTileAction();
			if(tileAction != nullptr)
			{
				menu = new Menu(hero, tileAction);
				menu->SetVisible(true);
			}
		}

		// Creepy
		if(aInput->KeyReleased(ALLEGRO_KEY_R))
		{
			RegenerateWorld();
		}
		if(aInput->KeyReleased(ALLEGRO_KEY_H))
		{
			hero->RegenerateCharacter();
		}
	}
}

void Game::Tick()
{
	if(title->nextPhrase == nullptr)
	{
		if(title->selected == 101)
		{
			CreateGame();
			title->nextPhrase = Phrase::CreatePhrase("Game started");
		}
		return;
	}

	srand(nextSeed);
	nextSeed = rand();

	if(menu->GetVisible()) return;

	currMap->Tick();

	if(transition->GetState() == TRANSITION_STATE_ON)
	{
		transition->Tick();
	}
	else
	{
		int x = hero->GetPositionX();
		int y = hero->GetPositionY();
		Node heroPos = {x,y};

		// Hero is in the world map and enters a city
		if(currMap == worldMap && !hero->IsMoving())
		{
			for(unsigned int i=0; i<worldMap->cities.size(); i++)
			{
				if(heroPos == worldMap->cities[i])
				{
					if(transition->GetState() == TRANSITION_STATE_OFF)
					{
						hero->Stop();
						transition->StartTransition(TRANSITION_ZOOM, true, 1.0f);

						inputLocked = true;
						return;
					}
					else if(transition->GetState() == TRANSITION_STATE_DONE)
					{
						worldMap->SaveHeroSpawn(heroPos);
						worldMap->SaveActualCityCoords(heroPos);

						// Create new map or load a pointer to existing one
						if(worldMap->childMaps.count(heroPos) == 0)
						{
							currMap = new CityMap(hero,worldMap->getSeed(x,y),worldMap->getLower(x,y));
							worldMap->childMaps[heroPos] = currMap;
						}
						else
							currMap = worldMap->childMaps[heroPos];

						Node heroSpawn = currMap->GetHeroSpawn(hero->GetFacing());
						hero->SetPosition(heroSpawn.x,heroSpawn.y);
						renderer->SetMap(currMap);

						transition->StartTransition(TRANSITION_ZOOM, false, 1.0f, true);

						inputLocked = false;
						return;
					}
				}
			} 
		}

		// Hero is in the city... 
		if(currMap->getMapType() == MAP_CITY && !hero->IsMoving())
		{
			//...and wants to get out
			if(!currMap->IsValid(x,y))
			{
				if(transition->GetState() == TRANSITION_STATE_OFF)
				{
					hero->Stop();
					transition->StartTransition(TRANSITION_FADE, true, 1.0f);

					inputLocked = true;
					return;
				}
				else if(transition->GetState() == TRANSITION_STATE_DONE)
				{
					currMap = worldMap;
					Node heroSpawn = currMap->GetHeroSpawn(hero->GetFacing());
					hero->SetPosition(heroSpawn.x,heroSpawn.y);
					renderer->SetMap(currMap);

					transition->StartTransition(TRANSITION_ZOOM, false, 1.0f, true);

					inputLocked = false;
					return;
				}
			}

			//...and wants to go to the maze
			else if(currMap->getUpper(x,y) == O_WELL)
			{
				if(transition->GetState() == TRANSITION_STATE_OFF)
				{
					hero->Stop();
					transition->StartTransition(TRANSITION_ZOOM, true, 1.0f);

					inputLocked = true;
					return;
				}
				else if(transition->GetState() == TRANSITION_STATE_DONE)
				{
					Node city = worldMap->GetActualCityCoords();

					// Create new map or load a pointer to existing one
					Map* current = currMap;
					Node fakeNode = {0,0};
					if(current->childMaps.count(fakeNode) == 0)
					{
						currMap = new MazeMap(hero,worldMap->getSeed(city.x,city.y),worldMap->getLower(city.x,city.y));
						currMap->parentMap = current;
						current->childMaps[fakeNode] = currMap;
					}
					else
						currMap = current->childMaps[fakeNode];

					Node heroSpawn = currMap->GetHeroSpawn(hero->GetFacing());
					hero->SetPosition(heroSpawn.x,heroSpawn.y);
					renderer->SetMap(currMap);

					transition->StartTransition(TRANSITION_ZOOM, false, 1.0f, true);

					inputLocked = false;
					return;
				}
			}
		}

		// Hero is in the maze... 
		if(currMap->getMapType() == MAP_MAZE && !hero->IsMoving())
		{
			//...and wants to get out
			if(currMap->getUpper(x,y) == O_LADDER)
			{
				if(transition->GetState() == TRANSITION_STATE_OFF)
				{
					hero->Stop();
					transition->StartTransition(TRANSITION_FADE, true, 1.0f);

					inputLocked = true;
					return;
				}
				else if(transition->GetState() == TRANSITION_STATE_DONE)
				{
					//int citySeed = worldMap->getSeed(worldMap->GetActualCityCoords().x,worldMap->GetActualCityCoords().y);
					//int cityBiome = worldMap->getLower(worldMap->GetActualCityCoords().x,worldMap->GetActualCityCoords().y);

					//map = new CityMap(hero,citySeed,cityBiome);
					currMap = currMap->parentMap;

					Node heroSpawn = currMap->GetHeroSpawn();
					hero->SetPosition(heroSpawn.x,heroSpawn.y-1);
					renderer->SetMap(currMap);

					transition->StartTransition(TRANSITION_ZOOM, false, 1.0f, true);

					inputLocked = false;
					return;
				}
			}
		}
	}
}
